diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-09-03 10:35:57 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-09-03 10:35:57 +0000 |
| commit | a2bc455f654e011c53968b0d3a14389d7259847e (patch) | |
| tree | 6ff60b8ef0880aaa4cf2c9d4f234772fb0a74537 /app/[lng] | |
| parent | bfe354f7633f62350e61eb784cbf1926079339d1 (diff) | |
(최겸) 구매 입찰 개발(벤더 응찰 개발 및 기본계약 요청 개발 필)
Diffstat (limited to 'app/[lng]')
| -rw-r--r-- | app/[lng]/evcp/(evcp)/bid/[id]/detail/page.tsx | 52 | ||||
| -rw-r--r-- | app/[lng]/evcp/(evcp)/bid/[id]/layout.tsx | 91 | ||||
| -rw-r--r-- | app/[lng]/evcp/(evcp)/bid/[id]/page.tsx | 52 | ||||
| -rw-r--r-- | app/[lng]/evcp/(evcp)/bid/[id]/pre-quote/page.tsx | 52 |
4 files changed, 201 insertions, 46 deletions
diff --git a/app/[lng]/evcp/(evcp)/bid/[id]/detail/page.tsx b/app/[lng]/evcp/(evcp)/bid/[id]/detail/page.tsx new file mode 100644 index 00000000..ac9b5df4 --- /dev/null +++ b/app/[lng]/evcp/(evcp)/bid/[id]/detail/page.tsx @@ -0,0 +1,52 @@ +import { Suspense } from 'react' +import { notFound } from 'next/navigation' +import { getBiddingDetailData } from '@/lib/bidding/detail/service' +import { BiddingDetailContent } from '@/lib/bidding/detail/table/bidding-detail-content' + +// 메타데이터 생성 +export async function generateMetadata({ params }: { params: Promise<{ id: string }> }) { + const { id } = await params + const parsedId = parseInt(id) + if (isNaN(parsedId)) return { title: '입찰 관리상세' } + + try { + const detailData = await getBiddingDetailData(parsedId) + return { + title: detailData.bidding ? `${detailData.bidding.title} - 입찰 관리상세` : '입찰 관리상세', + } + } catch { + return { title: '입찰 관리상세' } + } +} + +interface PageProps { + params: Promise<{ id: string }> +} + +export default async function Page({ params }: PageProps) { + const { id } = await params + const parsedId = parseInt(id) + + if (isNaN(parsedId)) { + notFound() + } + + // 통합 데이터 로딩 함수 사용 + const detailData = await getBiddingDetailData(parsedId) + + if (!detailData.bidding) { + notFound() + } + + return ( + <Suspense fallback={<div className="p-8">로딩 중...</div>}> + <BiddingDetailContent + bidding={detailData.bidding} + quotationDetails={detailData.quotationDetails} + quotationVendors={detailData.quotationVendors} + biddingCompanies={detailData.biddingCompanies} + prItems={detailData.prItems} + /> + </Suspense> + ) +} diff --git a/app/[lng]/evcp/(evcp)/bid/[id]/layout.tsx b/app/[lng]/evcp/(evcp)/bid/[id]/layout.tsx new file mode 100644 index 00000000..b675aed1 --- /dev/null +++ b/app/[lng]/evcp/(evcp)/bid/[id]/layout.tsx @@ -0,0 +1,91 @@ +import { Metadata } from "next" + +import { Separator } from "@/components/ui/separator" +import { SidebarNav } from "@/components/layout/sidebar-nav" +import { getBiddingById, getBiddingConditions } from "@/lib/bidding/service" +import { Bidding } from "@/db/schema/bidding" +import { Button } from "@/components/ui/button" +import { ArrowLeft } from "lucide-react" +import Link from "next/link" +import { BiddingInfoHeader } from "@/components/bidding/bidding-info-header" +import { BiddingConditionsEdit } from "@/components/bidding/bidding-conditions-edit" +export const metadata: Metadata = { + title: "Bidding Detail", +} + +export default async function SettingsLayout({ + children, + params, +}: { + children: React.ReactNode + params: { lng: string , id: string} +}) { + + // 1) URL 파라미터에서 id 추출, Number로 변환 + const resolvedParams = await params + const lng = resolvedParams.lng + const id = resolvedParams.id + + const idAsNumber = Number(id) + // 2) DB에서 해당 입찰 정보 조회 + const bidding: Bidding | null = await getBiddingById(idAsNumber) + const biddingConditions = await getBiddingConditions(idAsNumber) + + // 3) 사이드바 메뉴 + const sidebarNavItems = [ + { + title: "입찰 사전견적", + href: `/${lng}/evcp/bid/${id}/pre-quote`, + }, + { + title: "입찰 관리상세", + href: `/${lng}/evcp/bid/${id}/detail`, + }, + ] + + return ( + <> + <div className="container py-6"> + <section className="overflow-hidden rounded-[0.5rem] border bg-background shadow"> + <div className="hidden space-y-6 p-10 pb-16 md:block"> + {/* RFQ 목록으로 돌아가는 링크 추가 */} + <div className="flex items-center justify-end mb-4"> + <Link href={`/${lng}/evcp/bid`} passHref> + <Button variant="ghost" className="flex items-center text-primary hover:text-primary/80 transition-colors p-0 h-auto"> + <ArrowLeft className="mr-1 h-4 w-4" /> + <span>입찰 목록으로 돌아가기</span> + </Button> + </Link> + </div> + <div className="space-y-0.5"> + {/* 4) 입찰 정보가 있으면 번호 + 제목 + "상세 정보" 표기 */} + <h2 className="text-2xl font-bold tracking-tight"> + {bidding + ? `${bidding.biddingNumber ?? ""} - ${bidding.title}` + : "Loading Bidding..."} + </h2> + </div> + {/* 입찰 정보 헤더 */} + <BiddingInfoHeader bidding={bidding} /> + + {/* 입찰 조건 */} + {bidding && ( + <BiddingConditionsEdit + biddingId={bidding.id} + initialConditions={biddingConditions} + /> + )} + + <Separator className="my-6" /> + <div className="flex flex-col space-y-8 lg:flex-row lg:space-x-12 lg:space-y-0"> + <aside className="-mx-4 lg:w-1/5"> + <SidebarNav items={sidebarNavItems} /> + </aside> + <div className="flex-1 overflow-auto max-w-full">{children}</div> + </div> + </div> + </section> + </div> + </> + ) +}
\ No newline at end of file diff --git a/app/[lng]/evcp/(evcp)/bid/[id]/page.tsx b/app/[lng]/evcp/(evcp)/bid/[id]/page.tsx index e4051f9b..ca0788a5 100644 --- a/app/[lng]/evcp/(evcp)/bid/[id]/page.tsx +++ b/app/[lng]/evcp/(evcp)/bid/[id]/page.tsx @@ -1,52 +1,12 @@ -import { Suspense } from 'react' -import { notFound } from 'next/navigation' -import { getBiddingDetailData } from '@/lib/bidding/detail/service' -import { BiddingDetailContent } from '@/lib/bidding/detail/table/bidding-detail-content' - -// 메타데이터 생성 -export async function generateMetadata({ params }: { params: Promise<{ id: string }> }) { - const { id } = await params - const parsedId = parseInt(id) - if (isNaN(parsedId)) return { title: '입찰 상세' } - - try { - const detailData = await getBiddingDetailData(parsedId) - return { - title: detailData.bidding ? `${detailData.bidding.title} - 입찰 상세` : '입찰 상세', - } - } catch { - return { title: '입찰 상세' } - } -} +import { redirect } from 'next/navigation' interface PageProps { - params: Promise<{ id: string }> + params: Promise<{ lng: string; id: string }> } export default async function Page({ params }: PageProps) { - const { id } = await params - const parsedId = parseInt(id) - - if (isNaN(parsedId)) { - notFound() - } - - // 통합 데이터 로딩 함수 사용 - const detailData = await getBiddingDetailData(parsedId) - - if (!detailData.bidding) { - notFound() - } - - return ( - <Suspense fallback={<div className="p-8">로딩 중...</div>}> - <BiddingDetailContent - bidding={detailData.bidding} - quotationDetails={detailData.quotationDetails} - quotationVendors={detailData.quotationVendors} - biddingCompanies={detailData.biddingCompanies} - prItems={detailData.prItems} - /> - </Suspense> - ) + const { lng, id } = await params + + // 기본적으로 입찰 사전견적 페이지로 리다이렉트 + redirect(`/${lng}/evcp/bid/${id}/pre-quote`) } diff --git a/app/[lng]/evcp/(evcp)/bid/[id]/pre-quote/page.tsx b/app/[lng]/evcp/(evcp)/bid/[id]/pre-quote/page.tsx new file mode 100644 index 00000000..e2c22b22 --- /dev/null +++ b/app/[lng]/evcp/(evcp)/bid/[id]/pre-quote/page.tsx @@ -0,0 +1,52 @@ +import { Suspense } from 'react' +import { notFound } from 'next/navigation' +import { getBiddingDetailData } from '@/lib/bidding/detail/service' +import { BiddingDetailContent } from '@/lib/bidding/detail/table/bidding-detail-content' + +// 메타데이터 생성 +export async function generateMetadata({ params }: { params: Promise<{ id: string }> }) { + const { id } = await params + const parsedId = parseInt(id) + if (isNaN(parsedId)) return { title: '입찰 사전견적' } + + try { + const detailData = await getBiddingDetailData(parsedId) + return { + title: detailData.bidding ? `${detailData.bidding.title} - 입찰 사전견적` : '입찰 사전견적', + } + } catch { + return { title: '입찰 사전견적' } + } +} + +interface PageProps { + params: Promise<{ id: string }> +} + +export default async function Page({ params }: PageProps) { + const { id } = await params + const parsedId = parseInt(id) + + if (isNaN(parsedId)) { + notFound() + } + + // 통합 데이터 로딩 함수 사용 + const detailData = await getBiddingDetailData(parsedId) + + if (!detailData.bidding) { + notFound() + } + + return ( + <Suspense fallback={<div className="p-8">로딩 중...</div>}> + <BiddingDetailContent + bidding={detailData.bidding} + quotationDetails={detailData.quotationDetails} + quotationVendors={detailData.quotationVendors} + biddingCompanies={detailData.biddingCompanies} + prItems={detailData.prItems} + /> + </Suspense> + ) +} |
